package org.yn.user.service.impl;

import com.alibaba.fastjson.JSONObject;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.yn.auth.service.impl.LoginServiceImpl;
import org.yn.base.config.RegisterNoticeRabbitmqConfig;
import org.yn.base.constants.BaseConstants;
import org.yn.base.constants.WxConstants;
import org.yn.base.service.impl.BaseServiceImpl;
import org.yn.base.util.HttpUtil;
import org.yn.base.util.MD5Utils;
import org.yn.exception.AssertUtil;
import org.yn.exception.ResponseCode;
import org.yn.user.domain.Logininfo;
import org.yn.user.domain.User;
import org.yn.user.domain.Wxuser;
import org.yn.user.dto.BindSendSmsCodeDTO;
import org.yn.user.dto.EmailDTO;
import org.yn.user.dto.RegisterDTO;
import org.yn.user.dto.SmsDTO;
import org.yn.user.mapper.LogininfoMapper;
import org.yn.user.mapper.UserMapper;
import org.yn.user.mapper.WxuserMapper;
import org.yn.user.service.IUserService;

import java.util.Date;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author yn
 * @since 2023-08-26
 */
@Service
public class UserServiceImpl extends BaseServiceImpl<User> implements IUserService {

    @Autowired
    UserMapper userMapper;
    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    LogininfoMapper logininfoMapper;

    @Autowired
    WxuserMapper wxuserMapper;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    LoginServiceImpl loginServiceImpl;

    @Value("${MD5.preFix}")
    String preFix;

    @Value("${MD5.endFix}")
    String endFix;

    @Override
    public User selectByPhone(String phone) {
      User user= userMapper.selectByPhone(phone);
        return user;
    }

    /**
     * 注册
     * @param dto
     */
    @Override
    public void register(RegisterDTO dto) {
        // 参数校验
        // 两次密码是否一致
            //断言判断
        AssertUtil.isEq(dto.getPassword(),dto.getComformPassword(),ResponseCode.RESPONSE_CODE_40011);

        // 验证码是否一致
            //获取redis中的验证码
        String smsCodeByRedis = (String)redisTemplate.opsForValue().get(BaseConstants.REGISTER_SMS_VERIFY_CODE_KEY + dto.getPhone());
            // 断言是否为空，为空报错，说明没有或者过期
        String[] split = smsCodeByRedis.split(":");
       String smsCodeByRedis1=  split[0];
        AssertUtil.isNotBlank(smsCodeByRedis1, ResponseCode.RESPONSE_CODE_40017);
            // 不为空或者找到验证码后需要判断验证码是否匹配
        System.out.printf("dtoSmsCode："+dto.getSmsCode());
        System.out.printf("smsCodeByRedis："+smsCodeByRedis1);
        AssertUtil.isEqIgnoreCase(dto.getSmsCode(),smsCodeByRedis1,ResponseCode.RESPONSE_CODE_40020);


        // 校验用户名或邮箱是否存在
      int count=  userMapper.selectByNameOrEmail(dto.getUsername(),dto.getEmail());
            //校验
        AssertUtil.isZer0(count,ResponseCode.RESPONSE_CODE_40021);
        
        
        //保存注册信息   先存loginfo

        Logininfo logininfo = new Logininfo();
        logininfo.setPhone(dto.getPhone());
        logininfo.setUsername(dto.getUsername());
        logininfo.setEmail(dto.getEmail());
        logininfo.setType(1);//1为用户 0为管理员
        
            //密码处理
            //盐值
        String salt = UUID.randomUUID().toString();
        logininfo.setSalt(salt);   //保存
            //加密
        String password = MD5Utils.encrypByMd5(preFix + salt + dto.getPassword() + endFix);
        logininfo.setPassword(password);//保存加密后的密码

        logininfoMapper.insert(logininfo);

        //保存user
        User user = new User();
        BeanUtils.copyProperties(logininfo,user);
        user.setId(null);
        user.setState(1);
        user.setLogininfoId(logininfo.getId());
        user.setCreatetime(new Date());

        userMapper.insert(user);


        // 发送信息 --用消息队列 削峰  解耦

        // 发短信    由于用的是阿里云的发送短信的根据，发送的短信模板并不能自己定义，需要去申请，这里发送数字做验证
        SmsDTO smsDTO = new SmsDTO("1234", dto.getPhone());
        rabbitTemplate.convertAndSend(RegisterNoticeRabbitmqConfig.EXCHANGE_TOPICS_INFORM,"inform.sms",smsDTO);

        //发邮件
        EmailDTO emailDTO = new EmailDTO("注册成功通知", "恭喜！用户信息注册成功！！！", dto.getEmail());
        rabbitTemplate.convertAndSend(RegisterNoticeRabbitmqConfig.EXCHANGE_TOPICS_INFORM,"inform.email",emailDTO);


    }

    /**
     * 给新的微信用户绑定手机
     * @param dto
     * @return
     */
    @Override
    public Map<String, Object> wechatBind(BindSendSmsCodeDTO dto) {

        //  参数校验
        AssertUtil.isNotBlank(dto.getAccessToken(),ResponseCode.RESPONSE_CODE_500);
        AssertUtil.isNotBlank(dto.getOpenid(),ResponseCode.RESPONSE_CODE_500);
        AssertUtil.isNotBlank(dto.getVerifyCode(),ResponseCode.RESPONSE_CODE_500);
        AssertUtil.isNotBlank(dto.getPhone(),ResponseCode.RESPONSE_CODE_500);

        // 取redis中的验证码 比对
        String smsCodeByRedis = (String)redisTemplate.opsForValue().get(BaseConstants.BIND_SMS_VERIFY_CODE_KEY + dto.getPhone());
        AssertUtil.isNotBlank(smsCodeByRedis,ResponseCode.RESPONSE_CODE_40020);  //断言---40020验证码错误

        String smsCode = smsCodeByRedis.split(":")[0];
        AssertUtil.isEq(smsCode,dto.getVerifyCode(),ResponseCode.RESPONSE_CODE_500);

        // 数据库中查询查 该手机号是否存在
        //如果存在 是否已经绑定了微信用户
            // 绑定了微信用户则报错---此手机已经绑定
            //如果没有绑定微信用户,且改手机用户存在 则给绑定（
        // 如果不存在，则创建用户，给改用户 创建用户信息(取微信里面的信息),初始化密码


        // 2 根据accessToken和openid调用微信接口获取微信用户信息
        String getUserUrl = WxConstants.GET_USER_URL.replace("ACCESS_TOKEN", dto.getAccessToken())
                .replace("OPENID", dto.getOpenid());
        String wxUserStr = HttpUtil.httpGet(getUserUrl);
        Wxuser wxuserObj = wxUserStr2Obj(wxUserStr);   //类型转换


        // 1. 查看手机号用户是否存在
        User user = userMapper.selectByPhone(dto.getPhone());
        Logininfo logininfo = null;

        if (Objects.nonNull(user)){  //user不为空（存在）  1、绑定了微信用户  2、没有绑定微信用户
          //根据userid查询微信用户对象
          Wxuser wxuser= wxuserMapper.selectByUserId(user.getId());
          //断言    如果不为空这报错（不为空则说明wx用户已经存在)
            AssertUtil.isNull(wxuser,ResponseCode.RESPONSE_CODE_40024);

            // 然后就是 有用户但是没有绑定wx      先取出logininfo信息    后面再做绑定
            logininfo = logininfoMapper.loadByUserId(user.getId());


        }else {   //为空  说明没有注册过手机号，没有账号

            // 根据手机号初始化用户信息
            logininfo = createLogininfoByPhone(dto.getPhone());
            logininfoMapper.insert(logininfo);
            //初始化用户信息
            user = logininfo2User(logininfo);
            userMapper.insert(user);

        }
        // 新增微信用户信息并和手机号用户做绑定
        wxuserObj.setUserId(user.getId());
        wxuserMapper.insert(wxuserObj);
        // 免密登录
        Map<String, Object> map = loginServiceImpl.LoginSuccess(logininfo);
        return map;
    }

    private Wxuser wxUserStr2Obj(String wxUserStr) {
        JSONObject jsonObject = JSONObject.parseObject(wxUserStr);
        Wxuser wxuser = new Wxuser();
        wxuser.setOpenid(jsonObject.getString("openid"));
        wxuser.setNickname(jsonObject.getString("nickname"));
        wxuser.setSex(jsonObject.getInteger("sex"));
        wxuser.setAddress(jsonObject.getString("country")+jsonObject.getString("province")+jsonObject.getString("city"));
        wxuser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxuser.setUnionid(jsonObject.getString("unionid"));
        return wxuser;
    }

    /**
     * 根据手机号创建logininfo信息 ，初始化信息 用户名，密码，状态，类型
     * @param phone
     * @return
     */
    private Logininfo createLogininfoByPhone(String phone) {
        Logininfo logininfo = new Logininfo();
        logininfo.setUsername("wx_"+UUID.randomUUID().toString());
        logininfo.setPhone(phone);
        String salt = UUID.randomUUID().toString();
        // 项目中密码如果要默认设置的话,一般有手机号后六位,或者123456,如果是默认设置的密码,用户登录以后要让用户即使修改,
        // 一般来说设置完密码以后可以给用户发送短信告知一下
        String password = MD5Utils.encrypByMd5(preFix+salt+"123456"+endFix);;
        logininfo.setSalt(salt);
        logininfo.setPassword(password);
        logininfo.setType(1);
        logininfo.setDisable(1);
        return logininfo;
    }

    /**
     * 初始化user信息  将logininfo的一部分放进去
     * @param logininfo
     * @return
     */
    private User logininfo2User(Logininfo logininfo) {
        User user = new User();
        BeanUtils.copyProperties(logininfo,user);
        user.setId(null);
        user.setState(1);
        user.setCreatetime(new Date());
        user.setLogininfoId(logininfo.getId());
        return user;
    }

}
